<?php defined('SYS_PATH') or die('No direct script access.');
/**
 * Router class
 *
 * @author zendzhang<zendzhang@hotmail.com>
 * @version $Id: Router.php 9 2013-01-14 03:24:50Z zendzhang@hotmail.com $
 */
class TrueBlood_Router extends TrueBlood
{
    /**
     * Array of routes stored
     *
     * @var array
     */
    protected static $_routes = array();

    /**
     * Array of route matched
     *
     * @var array
     */
    protected static $_matched = array();

    /**
     * Constructor
     */
    public function __construct()
    {
        self::add(self::$load->config('routes'));

        self::log('debug', "TrueBlood_Router Class Initialized");
    }

    /**
     * Add routes
     *
     * @param array $routes
     * @return void
     */
    public static function add($routes)
    {
        $routes = (array) $routes;
        foreach ($routes as $name => $route)
        {
            $routes[$name]['match_replaced'] = self::replaceTags($route['match']);
        }
        self::$_routes = array_merge(self::$_routes, $routes);
    }

    /**
     * Get routes
     *
     * @param string $name
     * @param array $
     */
    public static function get($name = null)
    {
        if (null === $name)
        {
            return self::$_routes;
        }
        return isset(self::$_routes[$name]) ? self::$_routes[$name] : array();
    }

    /**
     * Tries to find a match for the current URI
     *
     * @return void
     */
    public static function match()
    {
        self::$_matched = self::get('default');
        $path = self::getURIPath();
        if (false !== $path)
        {
            $matched = false;
            foreach (self::$_routes as $name => $route)
            {
                // we dont need to check the default match
                if ('default' !== $name)
                {
                    if (preg_match('#^' . $route['match_replaced'] . '$#u', $path, $params))
                    {
                        self::log('debug', 'Match found: ' . $name . ' | URI: ' . $route['match']);
                        self::$_matched['name'] = $name;
                        self::$_matched['controller'] = $route['controller'];
                        self::$_matched['action'] = $route['action'];
                        array_shift($params);
                        self::$_matched['params'] = $params;
                        $matched = true;
                        break;
                    }
                }
            }
            // not found a match,try default MVC /controlerr/action/param1/param2/param3/..
            if (!$matched)
            {
                $params = self::getURIParams($path);
                if (count($params) > 0)
                {
                    self::$_matched['controller'] = array_shift($params);
                    self::$_matched['action'] = (count($params) > 0) ? array_shift($params) : 'index';
                    self::$_matched['params'] = (count($params) > 0) ? $params : false;
                }
            }
        }
        self::log('debug', 'Controller: ' . self::$_matched['controller'] . ' | Action: ' . self::$_matched['action'] . ' | Params: ' . serialize(self::$_matched['params']));
        return self::$_matched;
    }

    /**
     * Dispatch the routes to the right controller/action
     *
     * @return void
     * @throws TrueBlood_Exception
     */
    public function dispatch()
    {
        $route = self::match();
        $controller = ucfirst($route['controller']) . 'Controller';
        $action = $route['action'] . 'Action';

        if (self::$load->loadClass($controller, 'controllers') === false)
        {
            throw new TrueBlood_Exception("Unable to load controller:{$controller}");
        }
        $cls = new $controller();
        $func = array(&$cls, $action);
        if (!is_callable($func))
        {
            throw new TrueBlood_Exception("Unable to dispatch action:{$action}");
        }
        if (is_array($route['params']))
        {
            call_user_func_array($func, $route['params']);
        }
        else
        {
            call_user_func($func);
        }
    }

    /**
     * Get current matched route
     *
     * @return string
     */
    public static function getMatchedRoute()
    {
        return self::$_matched;
    }

    /**
     * Get the path from URL
     *
     * @return string
     */
    protected static function getURIPath()
    {
        $uri = parse_url(str_replace('/index.php', '', $_SERVER['REQUEST_URI']));
        $path = mb_strtolower($uri['path'], CHARSET);
        self::log('debug', 'URI path: ' . $path);
        return (!empty($path)) ? $path : false;
    }

    /**
     * get the params set in a URL for the default rewrite
     *
     * @param int $max
     * @return array
     */
    protected static function getURIParams($path, $max = 20)
    {
        $params = array();
        $i = 0;
        foreach (explode('/', $path) as $p)
        {
            if ($i >= $max)
            {
                return $params;
            }
            if ($p != '')
            {
                $params[] = self::cleanParam($p); //all the params are cleaned
                $i++;
            }
        }
        self::log('debug', 'URI params: ' . serialize($params));
        return $params;
    }

    /**
     * Replaces URI tags for regex
     *
     * @param array $route
     * @param string $
     */
    protected static function replaceTags($route)
    {
        $wildcard = array
        (
            '[any]',
            '[alphanum]',
            '[num]',
            '[alpha]'
        );
        $regex = array
        (
            '(.+)',
            '([a-z0-9]+)',
            '([0-9]+)',
            '([a-z]+)'
        );
        $route_r = str_replace($wildcard, $regex, $route);
        self::log('debug', $route . '--->' . $route_r);
        return $route_r;
    }

    /**
     * String cleaner, to prevent any kind of injection
     *
     * @param string $value
     */
    protected static function cleanParam($value)
    {
        return self::$load->request->clean($value);
    }

    /**
     * Returns an url given a "controller/action" and the prams to from it
     *
     * @param string $name
     * @param array $params
     */
    public static function url($name, $params = array())
    {
        $url = self::$load->request->domain() . "/$name";
        if (empty($params))
        {
            return $url;
        }
        if (defined('QUERY_STRINGS') && QUERY_STRINGS)
        {
            $segments = array();
            foreach ($params as $key => $value)
            {
                if (is_numeric($key) || empty($key))
                {
                    continue;
                }
                $segments[] = $key . '=' . urlencode($value);
            }
            return $url . '?' . implode('&', $segments);
        }
        return $url . '/' . implode('/', array_values($params));
    }
}

?>